/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

package ru.papergames.battleserver;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.mmocore.network.SelectorConfig;
import org.mmocore.network.SelectorThread;
import org.mmocore.network.SelectorThread.ClientType;
import ru.papergames.Config;
import ru.papergames.Server;
import ru.papergames.battleserver.model.UsersManager;
import ru.papergames.battleserver.model.battle.BattleManager;
import ru.papergames.battleserver.model.battle.npc.NpcsManager;
import ru.papergames.battleserver.model.battle.turnmanager.TurnManager;
import ru.papergames.battleserver.network.PaperGameClient;
import ru.papergames.battleserver.network.PaperGamePacketHandler;
import ru.papergames.battleserver.network.WebServerClient;
import ru.papergames.battleserver.network.WebServerPacketHandler;
import ru.papergames.battleserver.telnet.BattleTelnetServer;
import ru.papergames.battleserver.util.DeadLockDetector;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Calendar;


/**
 * This class ...
 *
 * @version $Revision: 1.29.2.15.2.19 $ $Date: 2005/04/05 19:41:23 $
 */
public class GameServer {
    private static final Logger _log = Logger.getLogger(GameServer.class.getName());

    private final SelectorThread<PaperGameClient> _selectorThread;
    private final SelectorThread<WebServerClient> _selectorThreadForWS;
    private final DeadLockDetector _deadDetectThread;
    public static GameServer gameServer;
    public static BattleTelnetServer gameTelnetServer;
    public static final Calendar dateTimeServerStarted = Calendar.getInstance();

    public long getUsedMemoryMB() {
        return (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1048576; // ;
    }

    public SelectorThread<PaperGameClient> getSelectorThread() {
        return _selectorThread;
    }

    public DeadLockDetector getDeadLockDetectorThread() {
        return _deadDetectThread;
    }

    public GameServer() throws Exception {
        long serverLoadStart = System.currentTimeMillis();

        gameServer = this;
        _log.info("used mem:" + getUsedMemoryMB() + "MB");

        if (Config.SERVER_VERSION != null) {
            _log.info("L2J Server Version:    " + Config.SERVER_VERSION);
        }
        if (Config.DATAPACK_VERSION != null) {
            _log.info("L2J Datapack Version:  " + Config.DATAPACK_VERSION);
        }

        BattleThreadPoolManager.getInstance();

        Runtime.getRuntime().addShutdownHook(Shutdown.getInstance());

        /**
         * Init classes for battle processing
         */
        BattleManager.getInstance().start();
        TurnManager.getInstance().start();
        UsersManager.getInstance().start();
        NpcsManager.getInstance().start();


        if (Config.DEADLOCK_DETECTOR) {
            _deadDetectThread = new DeadLockDetector();
            _deadDetectThread.setDaemon(true);
            _deadDetectThread.start();
        } else {
            _deadDetectThread = null;
        }
        System.gc();
        // maxMemory is the upper limit the jvm can use, totalMemory the size of
        // the current allocation pool, freeMemory the unused memory in the
        // allocation pool
        long freeMem = (Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory() + Runtime.getRuntime().freeMemory()) / 1048576;
        long totalMem = Runtime.getRuntime().maxMemory() / 1048576;
        _log.info("GameServer Started, free memory " + freeMem + " Mb of " + totalMem + " Mb");

//		_loginThread = LoginServerThread.getInstance();
//		_loginThread.start();


        /**
         * Selector thread for web server initializing.
         */
        final SelectorConfig scForWS = new SelectorConfig();
        scForWS.SLEEP_TIME = Config.SELECTOR_THREAD_SLEEP_FOR_WEB_SERVER;
        scForWS.MAX_READ_PER_PASS = 100;
        scForWS.MAX_SEND_PER_PASS = 100;
        final WebServerPacketHandler gphForWS = new WebServerPacketHandler();
        _selectorThreadForWS = new SelectorThread<WebServerClient>(scForWS, gphForWS, gphForWS, gphForWS, null, ClientType.TOMCAT);
//        InetAddress bindAddressForWS = InetAddress.getLocalHost();
        InetAddress bindAddressForWS = null;
        try {
            _selectorThreadForWS.openServerSocket(bindAddressForWS, Config.PORT_FOR_WEB_SERVER);
        } catch (IOException e) {
            _log.fatal("FATAL: Failed to open server socket. Reason: " + e.getMessage());
            if (Config.DEVELOPER) {
                e.printStackTrace();
            }
            System.exit(1);
        }
        _selectorThreadForWS.start();

        /**
         * Main selector thread(for flash clients) initializing.
         */
        final SelectorConfig sc = new SelectorConfig();
        sc.MAX_READ_PER_PASS = Config.MMO_MAX_READ_PER_PASS;
        sc.MAX_SEND_PER_PASS = Config.MMO_MAX_SEND_PER_PASS;
        sc.SLEEP_TIME = Config.MMO_SELECTOR_SLEEP_TIME;
        sc.HELPER_BUFFER_COUNT = Config.MMO_HELPER_BUFFER_COUNT;

        final PaperGamePacketHandler gph = new PaperGamePacketHandler();
        _selectorThread = new SelectorThread<PaperGameClient>(sc, gph, gph, gph, null, ClientType.FLASH);

        InetAddress bindAddress = null;
        if (!Config.GAMESERVER_HOSTNAME.equals("*")) {
            try {
                bindAddress = InetAddress.getByName(Config.GAMESERVER_HOSTNAME);
            } catch (UnknownHostException e1) {
                _log.fatal("WARNING: The GameServer bind address is invalid, using all avaliable IPs. Reason: " + e1.getMessage());

                if (Config.DEVELOPER) {
                    e1.printStackTrace();
                }
            }
        }

        try {
            _selectorThread.openServerSocket(bindAddress, Config.PORT_GAME);
        } catch (IOException e) {
            _log.fatal("FATAL: Failed to open server socket. Reason: " + e.getMessage());
            if (Config.DEVELOPER) {
                e.printStackTrace();
            }
            System.exit(1);
        }
        _selectorThread.start();
        _log.info("Maximum Numbers of Connected Players: " + Config.MAXIMUM_ONLINE_USERS);
        long serverLoadEnd = System.currentTimeMillis();
        _log.info("Server Loaded in " + ((serverLoadEnd - serverLoadStart) / 1000) + " seconds");
        _log.debug("*******************************last debug message************************************************");
        //TestBattleInitiator.initiateBattle();
    }

    private static void initLogging() {
//        PropertyConfigurator.configure("/config/log4j.properties");
        PropertyConfigurator.configure(GameServer.class.getResource("/config/log4j.properties"));
    }

    public static void main(String[] args) throws Exception {
        Server.serverMode = Server.MODE_GAMESERVER;

        initLogging();
/*
        // Local Constants
        final String LOG_FOLDER = "log"; // Name of folder for log file
        final String LOG_NAME = "./config/log.cfg"; // Name of log file

        */
/*** Main ***//*

        // Create log folder
        File logFolder = new File(Config.DATAPACK_ROOT, LOG_FOLDER);
        logFolder.mkdir();

        // Create input stream for log file -- or store file data into memory
        InputStream is = new FileInputStream(new File(LOG_NAME));
        LogManager.getLogManager().readConfiguration(is);
        is.close();
*/

        // Initialize config
        Config.load();
        gameServer = new GameServer();
        if (Config.TELNET_ENABLE) {
            gameTelnetServer = new BattleTelnetServer();
            gameTelnetServer.start();
        }
    }
}
